/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.generator;

/**
 *
 * @author Laurent WOUTERS
 */
public class ModelGenerator {
    public static final String ontologyOWL = "http://www.w3.org/2002/07/owl";
    public static final String owlThing = ontologyOWL + "#Thing";
    public static final String owlClass = ontologyOWL + "#Class";

    private Application application;
    private xowl.interpreter.Repository repository;
    private String basePackage;
    private java.util.Map<xowl.lang.owl2.Ontology, PackageGenerator> packages;
    private java.util.Map<xowl.lang.runtime.Class, ClassGenerator> anonymousClasses;
    private java.util.Map<xowl.lang.runtime.Datatype, JavaDatatype> datatypes;
    private java.util.Map<xowl.lang.runtime.NamedIndividual, InstanceGenerator> individuals;
    private java.util.Map<xowl.lang.owl2.Ontology, String> references;

    public xowl.interpreter.Repository getRepository() { return repository; }
    public String getBasePackage() { return basePackage; }
    public PackageGenerator getPackage(xowl.lang.owl2.Ontology ontology) { return packages.get(ontology); }
    public java.util.Collection<PackageGenerator> getPackages() { return packages.values(); }
    public ClassGenerator getClassGenerator(xowl.lang.runtime.Class c) {
        if (c == null)
            return null;
        if (c.getInterpretationOf() == null) {
            if (anonymousClasses.containsKey(c))
                return anonymousClasses.get(c);
            xowl.lang.owl2.Ontology onto = getClassGenerator_GetOntology(c);
            if (onto == null)
                return null;
            ClassGenerator gen = new ClassGenerator(application, packages.get(onto), c);
            packages.get(onto).addAnonymousClass(c, gen);
            anonymousClasses.put(c, gen);
            return gen;
        } else {
            for (PackageGenerator pack : packages.values()) {
                if (pack.getOWLOntology() == c.getInterpretationOf().getContainedBy())
                    return pack.getClass(c);
            }
            return null;
        }
    }
    private xowl.lang.owl2.Ontology getClassGenerator_GetOntology(xowl.lang.runtime.Class c) {
        if (!c.getAllClassUnionOf().isEmpty()) {
            java.util.List<ClassGenerator> results = new java.util.ArrayList<ClassGenerator>();
            for (xowl.lang.runtime.Class operand : c.getAllClassUnionOf()) {
                ClassGenerator temp = getClassGenerator(operand);
                if (temp != null)
                    results.add(temp);
            }
            if (results.isEmpty())
                return null;
            return results.get(0).getPackage().getOWLOntology();
        } else if (!c.getAllClassIntersectionOf().isEmpty()) {
            java.util.List<ClassGenerator> results = new java.util.ArrayList<ClassGenerator>();
            for (xowl.lang.runtime.Class operand : c.getAllClassIntersectionOf()) {
                ClassGenerator temp = getClassGenerator(operand);
                if (temp != null)
                    results.add(temp);
            }
            if (results.isEmpty())
                return null;
            return results.get(0).getPackage().getOWLOntology();
        } else if (!c.getAllClassOneOf().isEmpty()) {
            java.util.List<ClassGenerator> results = new java.util.ArrayList<ClassGenerator>();
            for (xowl.lang.runtime.Individual individual : c.getAllClassOneOf()) {
                if (individual instanceof xowl.lang.owl2.AnonymousIndividual)
                    continue;
                for (xowl.lang.runtime.Class type : individual.getAllClassifiedBy()) {
                    ClassGenerator temp = getClassGenerator(type);
                    if (temp != null)
                        results.add(temp);
                }
            }
        }
        return null;
    }
    public PropertyGenerator getPropertyGenerator(xowl.lang.runtime.Property prop) {
        for (PackageGenerator pack : packages.values()) {
            PropertyGenerator gen = pack.getProperty(prop);
            if (gen != null)
                return gen;
        }
        return null;
    }
    public JavaDatatype getJavaDatatype(xowl.lang.runtime.Datatype datatype) {
        if (datatypes.containsKey(datatype))
            return datatypes.get(datatype);
        JavaDatatype javaDatatype = JavaDatatype.createJavaDatatype(datatype);
        datatypes.put(datatype, javaDatatype);
        return javaDatatype;
    }
    public InstanceGenerator getInstanceGenerator(xowl.lang.runtime.NamedIndividual ind) {
        if (individuals.containsKey(ind))
            return individuals.get(ind);
        xowl.lang.runtime.Entity entity = ind.getInterpretationOf();
        for (xowl.lang.runtime.Interpretation interpretation : entity.getAllInterpretedAs())
        {
            if (interpretation instanceof xowl.lang.runtime.Class)
                return new InstanceGenerator(getOWLClassGenerator(), ind);
        }
        if (ind.getInterpretationOf().getHasIRI().getHasValue().equals(owlThing))
            return new InstanceGenerator(null, ind);
        return null;
    }
    public void addInstanceGenerator(xowl.lang.runtime.NamedIndividual ind, InstanceGenerator gen) { individuals.put(ind, gen); }
    private ClassGenerator getOWLClassGenerator() {
        for (xowl.lang.owl2.Ontology onto : packages.keySet()) {
            if (onto.getHasIRI().getHasValue().equals(ontologyOWL)) {
                ReflectionPackage packgen = (ReflectionPackage)packages.get(onto);
                return packgen.getClassClass(application);
            }
        }
        ReflectionPackage packgen = new ReflectionPackage(application, this, repository.resolveOntology(ontologyOWL));
        packages.put(repository.resolveOntology(ontologyOWL), packgen);
        return packgen.getClassClass(application);
    }
    
    public void addReference(xowl.lang.owl2.Ontology ontology, String _package) { references.put(ontology, _package); }
    
    public ModelGenerator(Application app, xowl.interpreter.Repository repo, String pack) {
        application = app;
        repository = repo;
        basePackage = pack;
        packages = new java.util.HashMap<xowl.lang.owl2.Ontology, PackageGenerator>();
        anonymousClasses = new java.util.HashMap<xowl.lang.runtime.Class, ClassGenerator>();
        datatypes = new java.util.HashMap<xowl.lang.runtime.Datatype, JavaDatatype>();
        individuals = new java.util.HashMap<xowl.lang.runtime.NamedIndividual, InstanceGenerator>();
        references = new java.util.HashMap<xowl.lang.owl2.Ontology, String>();
    }
    
    public void load() {
        for (xowl.lang.owl2.Ontology ontology : repository.getOntologies()) {
            String uri = ontology.getHasIRI().getHasValue();
            PackageGenerator packgen = null;
            if (uri.equals(ontologyOWL))
                packgen = new ReflectionPackage(application, this, ontology);
            else if (references.containsKey(ontology)) {
                String _package = references.get(ontology);
                String[] parts = _package.split("\\.");
                String last = parts[parts.length-1];
                packgen = new ReferencePackage(application, this, ontology, _package.substring(0, _package.length() - last.length() - 1), last);
            } else {
                String[] parts = uri.split("/");
                packgen = new PackageGenerator(application, this, ontology, parts[parts.length-1]);
            }
            packages.put(ontology, packgen);
        }
    }

    public void build() {
        // Build properties domain and range and inverses
        for (PackageGenerator pack : packages.values()) {
            for (PropertyGenerator gen : pack.getProperties()) {
                gen.buildDomainRange();
                gen.buildInverses();
            }
        }

        // Build class equivalency network
        for (PackageGenerator pack : packages.values()) {
            for (ClassGenerator gen : pack.getClasses()) {
                gen.buildEquivalents();
                gen.buildRestrictions();
            }
        }
        // Build class hierarchy
        for (PackageGenerator pack : packages.values())
            for (ClassGenerator gen : pack.getClasses())
                gen.buildHierarchy();
        for (ClassGenerator gen : anonymousClasses.values())
            gen.buildHierarchy();
        // Finalize hierarchy
        for (PackageGenerator pack : packages.values())
            for (ClassGenerator gen : pack.getClasses())
                gen.buildHierarchy_Union();
        for (ClassGenerator gen : anonymousClasses.values())
            gen.buildHierarchy_Union();

        // Build property equivalency network
        for (PackageGenerator pack : packages.values())
            for (PropertyGenerator gen : pack.getProperties())
                gen.buildEquivalents();
        // Build property hierarchy
        for (PackageGenerator pack : packages.values())
            for (PropertyGenerator gen : pack.getProperties())
                gen.buildHierarchy();

        // Build property interfaces and implemetation, build property graph
        for (PackageGenerator pack : packages.values())
            for (ClassGenerator gen : pack.getClasses())
                gen.buildInterfaces();
        for (ClassGenerator gen : anonymousClasses.values())
            gen.buildInterfaces();
        for (PackageGenerator pack : packages.values())
            for (ClassGenerator gen : pack.getClasses())
                gen.buildImplementations();
        for (ClassGenerator gen : anonymousClasses.values())
            gen.buildImplementations();
        for (PackageGenerator pack : packages.values())
            for (ClassGenerator c : pack.getClasses())
                for (PropertyImplementation impl : c.getPropertyImplementations())
                    impl.buildPropertyGraph();
        for (ClassGenerator gen : anonymousClasses.values())
            for (PropertyImplementation impl : gen.getPropertyImplementations())
                impl.buildPropertyGraph();
    }

    public void write(String folder, GenerationMode mode) throws java.io.IOException {
        folder += "src/";
        String[] subs = basePackage.split("\\.");
        for (String sub : subs)
            folder += sub + "/";
        java.io.File dir = new java.io.File(folder);
        dir.mkdirs();

        java.util.Random rand = new java.util.Random();
        int signature = rand.nextInt();
        
        if (mode == GenerationMode.Proxy)
            xowl.generator.proxy.ActivatorWriter.write(folder, this);
        for (PackageGenerator gen : packages.values())
            gen.write(folder, mode, signature);
    }
}
